package core

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"strconv"
)

const (
	MQ_Connect       = byte(0x10)
	MQ_ConnectAck    = byte(0x20)
	MQ_Pub           = byte(0x30) //客户端或服务端发布消息的核心报文，携带主题、QoS等级、消息标识符（QoS>0时）和负载
	MQ_FinishAck     = byte(0x31)
	MQ_PubAck        = byte(0x40) //QoS 1的确认响应，表示服务器已成功接收PUBLISH消息
	MQ_PubRec        = byte(0x50) //QoS 2的第一阶段确认，接收方发送此报文表示已收到PUBLISH消息
	MQ_PubRel        = byte(0x60) //QoS 2的第二阶段响应，发送方释放消息资源
	MQ_PubComp       = byte(0x70) //QoS 2的最终确认，表示消息传输完成
	MQ_Sub           = byte(0x80)
	MQ_SubAck        = byte(0x90)
	MQ_UnSub         = byte(0xa0)
	MQ_UnSubAck      = byte(0xb0)
	MQ_HeartBeat     = byte(0xc0)
	MQ_HeartBeatResp = byte(0xd0)
	MQ_DisConnect    = byte(0xe0)
	MQ_P2P           = byte(0xf0)
	MQ_P2PAck        = byte(0xf1)

	MQ_QOS_0 = byte(0x00) //最多一次，消息发送不确认，可能丢失
	MQ_QOS_1 = byte(0x01) // 至少一次，消息发送确认，可能重复
	MQ_QOS_2 = byte(0x02) // 恰好一次，消息发送4次握手确认，保证不重复，有且只接收一次

	MQ_ReSendNum  = 3
	MQ_OnlyOneUse = uint8(0x01)
)

// 错误代码
const (
	Error_Object_NotExist = "object not exist"
	Error_Protocol_Format = "protocol format error"
)

func StringToUint64(s string) (uint64, error) {
	// 尝试解析为十进制uint64
	num, err := strconv.ParseUint(s, 10, 64)
	if err != nil {
		return 0, fmt.Errorf("invalid uint64 string: %w", err)
	}
	return num, nil
}

// /////////////////////////////////////////////////////////////////////
type HeartBeatMsg struct {
	HeartBeatTime uint64 // 心跳时间戳
	ClientID      uint64 // 客户端ID
}

func DecodeHeartBeatMsg(data []byte) (*HeartBeatMsg, error) {
	if len(data) < 8 {
		return nil, errors.New("data too short to decode HeartBeatMsg")
	}
	if data[0] != MQ_HeartBeat {
		return nil, errors.New("invalid message type")
	}
	heartBeatTime := binary.BigEndian.Uint64(data[1:9])
	clientID, err := ReadUint64(data[9:])
	if err != nil {
		return nil, err
	}
	return &HeartBeatMsg{
		HeartBeatTime: heartBeatTime,
		ClientID:      clientID,
	}, nil
}

func EncodeHeartBeatMsg(msg *HeartBeatMsg) ([]byte, error) {
	bufs := bytes.NewBuffer(nil)

	if err := bufs.WriteByte(MQ_HeartBeat); err != nil {
		return nil, err
	}

	if err := binary.Write(bufs, binary.BigEndian, msg.HeartBeatTime); err != nil {
		return nil, err
	}

	if _, err := WriteUint64(bufs, msg.ClientID); err != nil {
		return nil, err
	}

	return bufs.Bytes(), nil
}

// ///////////////////////////////////////////////////////////////
type UserInfo struct {
	ClientID uint64 // 客户端ID,具有唯一性
	Password string
}

func NewConnectMsg(clientID uint64, password string) *UserInfo {
	return &UserInfo{ClientID: clientID, Password: password}
}

func DecodeConnectMsg(data []byte) (*UserInfo, error) {
	if data[0] != MQ_Connect {
		return nil, errors.New("invalid message type")
	}
	clientID, err := ReadUint64(data[1:])
	if err != nil {
		return nil, err
	}
	password, err := ReadString(data[9:])
	if err != nil {
		return nil, err
	}
	return &UserInfo{
		ClientID: clientID,
		Password: password,
	}, nil

}
func EncodeConnectMsg(msg *UserInfo) ([]byte, error) {
	bufs := bytes.NewBuffer(nil)

	if err := bufs.WriteByte(MQ_Connect); err != nil {
		return nil, err
	}

	if _, err := WriteUint64(bufs, msg.ClientID); err != nil {
		return nil, err
	}
	if _, err := WriteString(bufs, msg.Password); err != nil {
		return nil, err
	}
	return bufs.Bytes(), nil
}

// //////////////////////////////////////////////////////////////////
type ConnectAckMsg struct {
	UserID   uint64
	UserName string
	Result   uint8 // 0: success, 1: failure
	Error    error
	AES128   string
}

func DecodeConnectAckMsg(data []byte) (*ConnectAckMsg, error) {
	if len(data) < 5 {
		return nil, errors.New("data too short to decode ConnectAckMsg")
	}

	index := 0
	if data[0] != MQ_ConnectAck {
		return nil, errors.New("invalid message type")
	}
	index++
	clientID, err1 := ReadUint64(data[index:])
	if err1 != nil {
		return nil, err1
	}
	index += 8
	username, err2 := ReadString(data[index:])
	if err2 != nil {
		return nil, err2
	}
	index += 4 + len(username)
	result := data[index]
	index++
	var err error
	if len(data) > 9 {
		errMsg, err1 := ReadString(data[index:])
		if err1 != nil {
			return nil, err
		}
		index += 4 + len(errMsg)
		err = errors.New(errMsg)
	} else {
		err = nil
	}
	aes, err3 := ReadString(data[index:])
	if err3 != nil {
		return nil, err3
	}
	return &ConnectAckMsg{
		Result:   result,
		UserID:   clientID,
		UserName: username,
		Error:    err,
		AES128:   aes,
	}, nil
}
